socket.io 4.4.1に更新したらサーバー側からsocket切断してもクライアント側が切断されなくなった
最終的な正しい実装
以下は2022年1月の試行錯誤のログですshokai.icon
この脆弱性の方はEngine.IOを6.1.1に更新すれば解消する
たぶん脆弱性対応の影響だと思うが、socket.io server middlewareでdisconnectした後の挙動が変わったようだshokai.icon
changelogにはそれらしい内容が記載されていない
サーバー側からの切断
scrapboxでは
接続してきたsocket.ioのhttp部分のrequestを見て、user sessionに紐づけて
ログインユーザーでは無い場合、socketを即座に切断している
code:server/sockets/middlewares/index.js
export const disconnectIfNotUser = (socket, next) => {
if (!socket.user) return socket.disconnect() // 切断
next()
}
こういう状態になる
サーバー側
socket.ioレイヤーでは切断されている
io.sockets.socketsからはいなくなっている
engien.ioレイヤーでは切断されていない
health apiのio.engine.clientsCount.toString()のカウントが減っていない事で確認できる
クライアント側は
「自分は切断されていない」と認識しているようだ
切断されていないので、このままクライアント側トリガーのdisconnectが発生すると、自動的にreconnectしようとする
そしてreconnectは成功する
でもサーバー側ではsocket.ioレベルでは切断されていて、engine.ioレベルでは接続できている
なんだこの状態shokai.icon
ログインユーザーのcookieを持たないsocket.io接続が即座に切断されなくなっている
上の現象は普通には再現できない
scrapboxのclient jsは、自分がログインユーザーである事を確認してからsocket.ioを接続しに行く
情報漏洩は発生しない
非ログインユーザーにproject member用のメッセージが飛んでしまう様な問題は発生しない
socket.ioのレイヤーで接続中のクライアントとして認識されていない
roomへの参加時にはproject memberかどうかチェックしている
dissconnectIfNotUserはmiddlewareとして読み込まれている
その時点では接続できていない
実際socket.connected: falseになっている
未接続のsocketを切断しようとしているのだから、socketの下のengine.ioについては何もしなくなっているのかもしれないshokai.icon
その通りだった
code:js
public disconnect(close = false): this {
if (!this.connected) return this;
socket.ioだけでなくengine.ioも切断する
socket.connを使う
code:js
export const disconnectIfNotUser = (socket, next) => {
if (!socket.user) {
socket.disconnect()
socket.conn.close() // これを追加
return
}
next()
}
これで解決したshokai.icon
socket.disconnect()もいらないな
実質何も実行されない関数なので
でもなんとなく残しておいた方が良い気がする
意図が伝わりやすい